//
// Created by Gao Shihao on 2023/8/13.
//

#ifndef PLC2LLVM_SCOPEMANAGER_H
#define PLC2LLVM_SCOPEMANAGER_H

#include <stack>
#include <memory>
#include <plc2llvm/ScopeSystem/Scope.h>

namespace plcst {

    /*
     * 作用域管理器，单例类
     */
    class ScopeManager {
    public:
        ScopeManager();
        ~ScopeManager() = default;

        static ScopeManager& getScopeManager() {
            static ScopeManager manager;
            return manager;
        }

        ScopeManager(ScopeManager const &) = delete;
        ScopeManager& operator=(ScopeManager const &) = delete;

        void addNewScope(std::unique_ptr<Scope> scope);

        [[nodiscard]] plcst::Scope * getCurrentScope() const;

        void deleteCurrentScope();

        template <typename T>
        [[nodiscard]] std::shared_ptr<T> find(std::string_view itemName) const;

        Object* findObj(std::string_view objName);

        std::shared_ptr<Type> findType(std::string_view typeName);

        Scope* getGlobalScope();

    private:
        std::stack<std::unique_ptr<Scope>> scopeStack;
        Scope* globalScope;
        // 构造全局scope
        void init();

    };

    template <typename T>
    std::shared_ptr<T> plcst::ScopeManager::find(std::string_view itemName) const {

        Scope* currentScope = this->getCurrentScope();

        // 在当前作用域寻找
        if (auto item = currentScope->find<T>(itemName); item != nullptr)
            return item;
        if (auto item = currentScope->findCache<T>(itemName); item != nullptr)
            return item;

        Scope* localScope = currentScope->getParent();
        while ( localScope != nullptr) {
            // 在local scope中找到typename对应的type
            if (auto item = localScope->find<T>(itemName); item != nullptr) {
                // 加到currentScope的cache中
                currentScope->insertCache(item);
                return item;
            }

            // 在local scope 的cache中寻找
            if (auto item = localScope->findCache<T>(itemName); item != nullptr) {
                // 加到currentScope的cache中
                currentScope->insertCache(item);
                return item;
            }
            // 将localScope变成父作用域，进而在副作用域中进行寻找。
            localScope = localScope->getParent();
        }
        // TODO: 没有找到相应的T，报错
        std::string str;
        str.append("Can't not find the right type or object: ").append(itemName);
        throw SemanticError(str);
        // return nullptr;
    }

}



#endif //PLC2LLVM_SCOPEMANAGER_H
